<?php
/**
 * 文件上传类
 *
 * [Code]
 * $mimes = array('apk'=>'application/vnd.android.package-archive');
 * $h = App::H('Upload');
 * $h->init($mimes);
 * $h->setField('apk');
 * $h->setDir('');
 * $h->save();
 * [/Code]
 *
 * @version $Id: Upload.php 90 2013-05-21 08:40:21Z linsir123 $
 * @package Helper
 */
class HelperUpload
{
	/**
	 * 异常描述
	 *
	 * @var array
	 */
	public $message = array(
		1 => '没有上传文件或非法请求',
		2 => '文件上传到服务器失败',
		3 => '文件大小超出限制',
		4 => '文件类型有误',
		5 => '上传的图片有误',
		6 => '图片尺寸有误',
	);

	/**
	 * 允许上传数据类型(此项为必填项)
	 * // 格式：后缀 => 类型(可为空)
	 * // 如：'apk' => 'application/vnd.android.package-archive'
	 *
	 * @var array
	 */
	public $mimes = array();

	/**
	 * 允许上传文件大小(0为不限制大小)
	 * // 单位为字节
	 *
	 * @var int
	 */
	public $maxSize = 0;

	/**
	 * 上传的图片文件尺寸限定方式
	 * // 宽，高
	 * // 回调
	 *
	 * @var array
	 */
	public $imageArea = array();

	/**
	 * 文件上传的路径
	 *
	 * @var string
	 */
	private $_saveDir;

	/**
	 * 上传文件原始数据
	 *
	 * @var string
	 */
	private $_file;

	/**
	 * 上传文件的后缀
	 *
	 * @var string
	 */
	private $_fileExt;

	/**
	 * 文件上传后保存的文件名
	 *
	 * @var string
	 */
	private $_fileName;

	/**
	 * 上传状态码(0成功,非0失败)
	 *
	 * @var int
	 */
	private $_code;

	/**
	 * 上传异常描述
	 *
	 * @var string
	 */
	private $_exception;

	/**
	 * 初始化
	 *
	 * @param array $mimes 允许上传的MIME数据
	 * @param int $maxSize 允许上传的最大大小
	 */
	public function init($mimes, $maxSize = 0)
	{
		$this->mimes = $mimes;
		$this->maxSize = $maxSize;
	}

	/**
	 * 设置文件域名称，用于配置当前上传的文件
	 * // 此时就会检查文件的可用性
	 *
	 * @param string $field 文件域名称
	 * @param string $index 序号
	 */
	public function setField($field, $index = null)
	{
		$data = array();
		if (isset($_FILES[$field])) {
			$data = $_FILES[$field];
			if (! is_null($data[$index]) && isset($data[$index]))
				$data = $data[$index];
		}

		$this->_file = $data;
		$this->_exception = '';
		$this->_code = 0;

		///
		$this->_checkFile();
	}

	/**
	 * 设置上传目录
	 *
	 * @param string $saveDir 目录物理地址
	 */
	public function setDir($saveDir)
	{
		$saveDir = str_replace(array('/', '\\'), DS, $saveDir);
		$this->_saveDir = ((substr($saveDir, - 1) == DS) ?
			substr($saveDir, 0, - 1) : $saveDir) . DS;
	}

	/**
	 * 保存文件
	 *
	 * @param string $fileName 文件名(不需要后缀)
	 *
	 * @return bool
	 */
	public function save($fileName = '')
	{
		if (empty($this->_code)) {
			$fileName or $fileName = md5($this->_file['name']);
			$this->_fileName = $fileName . '.' . $this->_fileExt;
			$desc = $this->_saveDir . $this->_fileName;

			///
			return move_uploaded_file($this->_file['tmp_name'], $desc);
		}

		return false;
	}

	/**
	 * 取得异常代码
	 */
	public function getCode()
	{
		return $this->_code;
	}

	/**
	 * 取得异常信息
	 */
	public function getException()
	{
		return $this->_exception;
	}

	/**
	 * 取得上传文件的原始信息
	 */
	public function getFile()
	{
		return $this->_file;
	}

	/**
	 * 取得文件的保存名称
	 * // 当文件保存成功后，取得物理地址
	 *
	 * @param bool $full 是否完整地址
	 *
	 * @return string
	 */
	public function getSaveName($full = true)
	{
		return ($full ? $this->_saveDir : '') . $this->_fileName;
	}

	/**
	 * 验证上传文件
	 */
	private function _checkFile()
	{
		try {
			if ($code = $this->_checkError())
				throw new Exception($this->message[$code], $code);

			///
			foreach ($this->mimes as $ext => $mime) {
				if (preg_match('!\.(' . $ext . ')$!i',
					strtolower($this->_file['name']), $m)
				) {
					$this->_fileExt = strtolower($m[1]);
					break;
				}
			}

			///
			if (empty($this->_fileExt))
				throw new Exception($this->message[4], 4);

			///
			if (! empty($this->maxSize)) {
				if ($this->_file['size'] > $this->maxSize)
					throw new Exception($this->message[3], 3);
			}

			///
			if ($code = $this->_checkImage())
				throw new Exception($this->message[$code], $code);

		} catch (Exception $e) {
			$this->_code = $e->getCode();
			$this->_exception = $e->getMessage();
		}
	}

	/**
	 * 检测文件上传的错误代码
	 *
	 * @return int
	 */
	private function _checkError()
	{
		if (empty($this->_file)
			|| strtoupper($_SERVER['REQUEST_METHOD']) != 'POST'
		)
			return 1;

		///
		if (! is_uploaded_file($this->_file['tmp_name'])
			|| $this->_file['error'] != UPLOAD_ERR_OK
		) {
			switch ($this->_file['error']) {
				case UPLOAD_ERR_INI_SIZE:
				case UPLOAD_ERR_FORM_SIZE:
					return 3;

				case UPLOAD_ERR_NO_FILE:
					return 1;

				default:
					return 2;
			}
		}

		return 0;
	}

	/**
	 * 检查上传的图片资源是否正常
	 *
	 * @return int
	 */
	private function _checkImage()
	{
		if (preg_match('/^(jpg|jpeg|gif|png)$/i', $this->_fileExt)) {
			$size = getimagesize($this->_file['tmp_name']);
			if (! empty($size))
				$flag = array_search($size['mime'], $this->mimes) === false;
			else
				$flag = true;

			if ($flag)
				return 5;

			///
			if (! empty($this->imageArea)) {
				if (is_callable($this->imageArea))
					$flag = call_user_func_array($this->imageArea, array($size));
				else {
					list($w, $h) = $this->imageArea;
					list($width, $height) = $size;
					$flag = intval($w) == intval($width);
					$flag = $flag && (intval($h) == intval($height));
				}

				if (! $flag)
					return 6;
			}
		}

		return 0;
	}
}
